﻿using System;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Net.Mail;

namespace Altairis.MailToolkit.Transport {

    /// <summary>
    /// Transport provider using .NET SmtpClient, allowing to set app-relative message file path
    /// </summary>
    public class SmtpClientTransportProvider : TransportProvider {
        private const int DEFAULT_HANDLER_TIME_WINDOW = 30;

        /// <summary>
        /// Initializes the provider.
        /// </summary>
        /// <param name="name">The friendly name of the provider.</param>
        /// <param name="config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
        /// <exception cref="System.Configuration.Provider.ProviderException">The customPickupDirectory can contain relative paths only in web applications.</exception>
        public override void Initialize(string name, NameValueCollection config) {
            base.Initialize(name, config);

            // Set custom pickup folder
            this.CustomPickupDirectory = config.GetConfigValue("customPickupDirectory", string.Empty);
            if (this.CustomPickupDirectory.StartsWith("~/")) {
                if (System.Web.HttpContext.Current == null) throw new ProviderException("The customPickupDirectory can contain relative paths only in web applications.");
                this.CustomPickupDirectory = System.Web.HttpContext.Current.Server.MapPath(this.CustomPickupDirectory);
            }

            // Set handler options
            this.HandlerEnabled = config.GetConfigValue("handlerEnabled", false);
            if (this.HandlerEnabled) {
                this.HandlerIpAddresses = config.GetConfigValue("handlerIpAddresses", string.Empty).Split(',');
                this.HandlerTimeWindow = config.GetConfigValue("handlerTimeWindow", DEFAULT_HANDLER_TIME_WINDOW);
                if (this.HandlerTimeWindow < 0) throw new ProviderException("The handlerTimeWindow must be positive integer.");

                // Set handler secret
                var keyString = config.GetConfigValue("handlerSecret", string.Empty);
                if (string.IsNullOrEmpty(keyString)) throw new ProviderException("The handlerSecret attribute must be specified when enablerEnabled is set to true.");
                try {
                    this.HandlerSecret = Convert.FromBase64String(keyString);
                }
                catch (Exception ex) {
                    throw new ProviderException("The handlerSecret is not valid Base64.", ex);
                }
            }
            else {
                config.Remove("handlerIpAddresses");
                config.Remove("handlerSecret");
                config.Remove("handlerTimeWindow");
            }
        }

        /// <summary>
        /// Gets the custom pickup directory.
        /// </summary>
        /// <value>
        /// The custom pickup directory.
        /// </value>
        public string CustomPickupDirectory { get; private set; }

        /// <summary>
        /// Gets a value indicating whether the mail pickup handler is enabled.
        /// </summary>
        /// <value>
        ///   <c>true</c> if the mail pickup handler is enabled; otherwise, <c>false</c>.
        /// </value>
        public bool HandlerEnabled { get; private set; }

        /// <summary>
        /// Gets the list of IP addresses allowed to use the pail pickup handler.
        /// </summary>
        /// <value>
        /// The list of IP addresses allowed to use the pail pickup handler.
        /// </value>
        public string[] HandlerIpAddresses { get; private set; }

        /// <summary>
        /// Gets the handler secret key.
        /// </summary>
        /// <value>
        /// The handler secret key.
        /// </value>
        public byte[] HandlerSecret { get; private set; }

        /// <summary>
        /// Gets the size of allowed clock skew for handler authentication in seconds.
        /// </summary>
        /// <value>
        /// The size of allowed clock skew for handler authentication in seconds.
        /// </value>
        public int HandlerTimeWindow { get; private set; }

        /// <summary>
        /// Sends the message using current transport provider.
        /// </summary>
        /// <param name="msg">The message to be sent.</param>
        public override void SendMessage(MailMessage msg) {
            using (var mx = new SmtpClient()) {
                if (!string.IsNullOrWhiteSpace(this.CustomPickupDirectory)) {
                    mx.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
                    mx.PickupDirectoryLocation = this.CustomPickupDirectory;
                }
                mx.Send(msg);
            }
        }
    }
}